using System;
using System.Collections.Generic;
using System.Text;
using RoardersAgentSim.Map;

namespace RoardersAgentSim.Agent
{
    public class RoadLink
    {
        // input attributes
        //public int[] H;             // set of zones
        #region implementation attributes
        //public double[] d_h;        // distance of zone h to risk area
        //public int[] h_l;           // locations
        //public int[] A_plus;        // sets of in-going links
        //public int[] A_minus;       // sets of out-going links
        //public double[] c;          // travel cost (time) of link k
        /// <summary>
        /// Travel cost (time) of link
        /// </summary>
        public double c;
        //public double[] v_bar;      // max speed
        /// <summary>
        /// max speed
        /// </summary>
        public double v_bar;
        /// <summary>
        /// Link speed at time t
        /// </summary>
        public double v_kt;
        /// <summary>
        /// The physical link
        /// </summary>
        public Link Link;
        //public int[] rho;           // type => speed-density relation
        /// <summary>
        /// type => speed-density relation
        /// </summary>
        public int rho;
        //public double[] y_bar;      // nominal capacity
        /// <summary>
        /// nominal capacity
        /// </summary>
        public double y_bar;
        //public double[] y;          // capacity considering damage or emergency transport
        /// <summary>
        /// capacity considering damage or emergency transport
        /// </summary>
        public double y;
        //type in Link
        //public int[] type;          // the set of links is doubled (type = 0 by foot; type = 1 by car)

        // derived attributes
        //public double[, ,] g_walk;  // predicted travel time by walking from zone h to zone j at simulation time step t
        //public double[, ,] g_car;   // predicted travel time by car from zone h to zone j at simulation time step t
        /// <summary>
        /// predicted travel time by walking from zone h (of the link) to shelter j(which is key) at simulation time step t
        /// </summary>
        public Dictionary<int, double> g_walk;
        /// <summary>
        /// predicted travel time by car from zone h (of the link) to shelter j(which is key) at simulation time step t
        /// </summary>
        public Dictionary<int, double> g_car;


        //public double[] q;          // flows
        //public double[,] q_hs;      // flow from zone h to zone s
        //public double[, ,] q_hsk;   // flow from zone h to zone s on link k
        //public int[,] delta_hj;     // whether demand from zone h is assignable to shelter j
        //public int[, ,] delta_hjk;  // whether the demand from h to j can use arc k

        /// <summary>
        /// flow
        /// </summary>
        public double q;
        /// <summary>
        /// flow to shelter s on link k
        /// </summary>
        public Dictionary<int, int> q_hsk;
        /// <summary>
        /// whether demand from current zone h is assignable to shelter j
        /// </summary>
        public int[] delta_hj;
        /// <summary>
        /// whether the demand from current zone h to j can use arc k
        /// </summary>
        public int[,] delta_hjk;
        #endregion

        #region log helper attributes
        /// <summary>
        /// link description
        /// </summary>
        public string Description
        {
            get
            {
                string s = "";
                switch (this.Link.Type)
                {
                    case LinkType.CAR_LINK:
                        s = "(CAR link)";
                        break;
                    default:
                        s = "(WALK link)";
                        break;
                }
                return this.Link.Source.NodeID + "-" + this.Link.Destination.NodeID + s;
            }
        }

        #endregion

        #region export attributes

        internal double TotalFlow
        {
            private set;
            get;
        }

        internal double MaxFlow
        {
            private set;
            get;
        }

        #endregion


        #region constructor
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="link"></param>
        public RoadLink(Link link)
        {
            this.Link = link;
            Initialise();
        }
        /// <summary>
        /// Road link constructor
        /// </summary>
        /// <param name="linkMapping"></param>
        public RoadLink(CSVUtils.CSVMapping.LinkMapping linkMapping)
        {
            this.Link = new Link(linkMapping);
            this.v_bar = linkMapping.Vmax;
            this.rho = linkMapping.Rho;
            this.y_bar = linkMapping.NominalCapacity;
            this.y = linkMapping.DamageCapacity;
            Initialise();
        }
        #endregion

        // ----------------------------------------------

        public void Initialise()
        {
            q = 0;
            TotalFlow = 0;
            MaxFlow = 0;
            g_car = new Dictionary<int, double>();
            g_walk = new Dictionary<int, double>();
        }

        /// <summary>
        /// Calculated and store link speed
        /// </summary>
        public void ComputeLinkSpeed() // A3.1
        {
            //if we are dealing with a car link
            if (this.Link.Type == LinkType.CAR_LINK)
            {
                if (this.q == this.y)//if the link is full->it should never be bigger
                {
                    this.v_kt = Utils.GetCarCongestionSpeed();
                }
                else
                {
                    double velocity = this.v_bar * Math.Pow((1 - this.q / this.y), this.rho);
                    if (velocity == Double.NaN || velocity < 0)//this should never happen
                    {
                        LogHelper.LogStep("Check if damage capacity = 0 for link" + this.Description + " . Currently the velocity= " + velocity.ToString()+ " ; will be now 0",true);
                        this.v_kt = 0;
                    }
                    else
                    {
                        if (velocity == 0)
                            velocity = Utils.GetCarCongestionSpeed();
                        this.v_kt = velocity;
                    }
                }
            }
            else//if we are dealing with a walking link, there is no need to calculate the speed
            {
                this.v_kt = this.v_bar;
            }
        }
        /// <summary>
        /// Check if the current link is blocked (meaning it should not add anymore population groups)
        /// </summary>
        /// <returns></returns>
        public bool IsBlocked()
        {
            if (this.q >= this.y || this.y == 0 || (this.y - this.q < (double)PopulationManager.GROUPSIZE))
                return true;
            return false;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private double GetEstimatedTravelTime()
        {
            if (this.IsBlocked())
                return Double.PositiveInfinity;
            return this.Link.Length / (this.v_kt * Utils.GetTimeConversion());
        }

        /// <summary>
        /// Get CK CAR
        /// </summary>
        /// <param name="zetabar"></param>
        /// <returns></returns>
        internal double getCKCar(double zetabar)
        {
            double ck_car = this.GetEstimatedTravelTime() + zetabar;
            return ck_car;
        }
        /// <summary>
        /// Get CK Walk
        /// </summary>
        /// <param name="zetabar"></param>
        /// <returns></returns>
        internal double getCKWalk(double zetabar)
        {
            double ck_walk = this.GetEstimatedTravelTime() + zetabar;
            return ck_walk;
        }
        /// <summary>
        /// Calculate the road link cost for optimal cost path choice
        /// </summary>
        /// <param name="aux"></param>
        /// <returns></returns>
        public double CalculateCost(double aux)
        {
            return this.GetEstimatedTravelTime() + aux;
        }
        /// <summary>
        /// Set the zone
        /// </summary>
        /// <param name="z"></param>
        internal void SetZone(Zone z)
        {
            this.Link.H = z;
        }
        /// <summary>
        /// Procedure to manage the flow update
        /// </summary>
        /// <param name="numberOfPeople"></param>
        internal void UpdateFlow(int numberOfPeople)
        {
            this.q += numberOfPeople;
            if (this.MaxFlow < this.q)
                this.MaxFlow = q;
            if (numberOfPeople >= 0) 
            this.TotalFlow += numberOfPeople;
        }
    }
}
